#pragma once

#include "Mesh.hpp"

#include "D3D11Declarations.hpp"
#include "D3D11Exception.hpp"




namespace Engine
{
	namespace Video
	{
		class D3D11Mesh
			: public Mesh
		{
		public:

			SHARED(D3D11Mesh)
			typedef boost::function<void (weak_ptr)>   notify_t;

		protected:

			/**
			 * Create a new Mesh.
			 */
			D3D11Mesh(const notify_t& destroyedFn,
				const ID3D11DevicePtr& device,
				const ID3D11DeviceContextPtr& context,
				uint32 sizePerVertex, uint32 sizePerIndex, 
				const StaticReflection& vertexReflection,
				Format::Type indexType,
				Primitive::Type primitiveType,
				Buffer::Type bufferType);

			void post_create(ptr _this);

		public:

			MAKE_SHARED9(D3D11Mesh, notify_t, ID3D11DevicePtr, ID3D11DeviceContextPtr, uint32, uint32, StaticReflection, Format::Type, Primitive::Type, Buffer::Type);
			~D3D11Mesh();



			/**
			 * Test if this mesh is a dynamic or a static one.
			 */
			Buffer::Type usage() const;

			/**
			 * Get the type of primitive, this mesh is going to render.
			 */
			Primitive::Type primitiveType() const;

			/**
			 * Get a pointer to the InputSignature of the vertexbuffer.
			 */
			StaticReflection reflect() const;


			void stream(const ID3D11DeviceContextPtr& context) const;
			void draw(const ID3D11DeviceContextPtr& context) const;



			/**
			 * Get a table of D3D11_INPUT_ELEMENT_DESC elements that describe the given
			 * Reflection object.
			 */
			static D3D11_INPUT_ELEMENT_DESC_ARRAY inputDescription(const StaticReflection& reflection);

		private:

			/**
			 * Create a Vertexbuffer from the given list of vertices.
			 */
			void createVertexBuffer(const void* vertexData, uint32 vertexSize);

			/**
			 * Create an IndexBuffer from the given list of indices.
			 */
			void createIndexBuffer(const void* indexData, uint32 indexSize);

			/**
			 * These functions update the Mesh with the given set of vertices and indices.
			 * Throws an exception when this is a static mesh.
			 */
			void update(const void* vertexData, uint32 vertexSize, const void* indexData, uint32 indexSize, uint32 sizePerIndex, Format::Type indexType);

			/**
			 * Updates the VertexBuffer with the given data.
			 * Recreates the buffer when it's too small.
			 */
			void update_vertices(const void* vertexData, uint32 vertexSize);

			/**
			 * Updates the IndexBuffer with the given data.
			 * Recreates the buffer when it's too small.
			 */
			void update_indices(const void* indexDat, uint32 indexSize);



			/**
			 * Get the size of one vertex of this mesh.
			 */
			uint32 sizePerVertex() const;

			/**
			 * Get the size of one index of this mesh.
			 */
			uint32 sizePerIndex() const;



			/**
			 * Create a buffer description and fill in all information that can be extracted
			 * from the mesh type. (It's not ready to be used yet).
			 */
			static D3D11_BUFFER_DESC fromUsage(Type type);

			/**
			 * Get the DXGI_FORMAT identifier for the given Format::Type.
			 */
			static DXGI_FORMAT formatType(Format::Type type);

			/**
			 * Get the Topology 
			 */
			static D3D11_PRIMITIVE_TOPOLOGY topologyFromPrimitive(Primitive::Type type);

		private:

			weak_ptr                 m_this;
			notify_t                 m_destroyedFn;

			ID3D11DevicePtr          m_device;
			ID3D11DeviceContextPtr   m_context;

			ID3D11BufferPtr          m_vertexBuffer;
			ID3D11BufferPtr          m_indexBuffer;

			uint32                   m_vertexBufferSize;
			uint32                   m_indexBufferSize;

			StaticReflection         m_vertexReflection;
			Format::Type             m_indexType;
			Primitive::Type          m_primitiveType;
			Buffer::Type             m_bufferType;

			uint32                   m_sizePerVertex;
			uint32                   m_sizePerIndex;

			uint32                   m_numVertices;
			uint32                   m_numIndices;
			uint32                   m_numPrimitives;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
